package com.bes.bessdk.connect;

import static android.content.Context.USB_SERVICE;

import static com.bes.bessdk.BesSdkConstants.BES_CONNECT_ERROR;
import static com.bes.bessdk.BesSdkConstants.BES_CONNECT_SUCCESS;
import static com.bes.bessdk.BesSdkConstants.BES_SAVE_LOG_OTA;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.bes.bessdk.BesSdkConstants;
import com.bes.bessdk.service.base.BesServiceConfig;
import com.bes.bessdk.utils.ArrayUtil;
import com.bes.bessdk.utils.LogUtils;
import com.bes.bessdk.utils.SPHelper;
import com.bes.sdk.connect.DeviceConnector;
import com.bes.sdk.device.HmDevice;
import com.bes.sdk.message.BaseMessage;
import com.bes.sdk.scan.ScanManager;
import com.bes.sdk.utils.DeviceProtocol;

import java.util.HashMap;
import java.util.List;

public class UsbConnector implements DeviceConnector {
    protected static final String TAG = UsbConnector.class.getSimpleName();

    private static volatile UsbConnector sConnector;
    private static Context mContext;
    private static HmDevice mHmDevice;
    private static ConnectionListener mConnectionListener;

    private static UsbManager mUsbManager;
    private static UsbDevice mUsbDevice;
    private static UsbInterface mInterface;
    private static UsbEndpoint inEndpoint;
    private static UsbEndpoint outEndpoint;
    private static UsbDeviceConnection mDeviceConnection;

    private static HashMap<String, UsbDevice> deviceList;

    public static UsbConnector getsConnector(Context context, BesServiceConfig serviceConfig) {
        if (sConnector == null) {
            synchronized (UsbConnector.class) {
                sConnector = new UsbConnector();
                mUsbManager = (UsbManager) context.getSystemService(USB_SERVICE);
            }
        }
        if (context != null) {
            mContext = context;
        }
        return sConnector;
    }

    @Override
    public List<DeviceProtocol> getSupportedProtocols() {
        return null;
    }

    @Override
    public boolean isProtocolSupported(DeviceProtocol deviceProtocol) {
        return false;
    }

    @Override
    public void connect(HmDevice device) {

    }

    @Override
    public void connect(HmDevice device, ConnectionListener connectionListener) {
        mConnectionListener = connectionListener;
        mHmDevice = device;
        inEndpoint = null;
        outEndpoint = null;
        if (!deviceList.isEmpty()) {
            for (UsbDevice usbDevice : deviceList.values()) {
                if (device.getDevicePid().equals(usbDevice.getProductId() + "") && device.getDeviceVid().equals(usbDevice.getVendorId() + "")) {
                    mUsbDevice = usbDevice;
                    getUsbPermission(mUsbDevice);
                    threadReadData.start();
                    return;
                }
            }
        }
        if (mConnectionListener != null)
            mConnectionListener.onStatusChanged(device, BES_CONNECT_ERROR, DeviceProtocol.PROTOCOL_USB);
    }

    @Override
    public void disconnect(HmDevice device) {

    }

    @Override
    public void registerConnectionListener(ConnectionListener connectionListener) {

    }

    @Override
    public void unregisterConnectionListener(ConnectionListener connectionListener) {

    }

    public boolean write(byte[] data) {
        LOGAAA("USB write", "----------before---dataLen" + data.length);
        int ncountWrite = mDeviceConnection.bulkTransfer(outEndpoint, data, data.length,100);
        LOGAAA("USB write", "----------after");
        LOG(TAG, "USB Write: --------" + ncountWrite);
        LOG(TAG, "write length: --------" + data.length);
        if (ncountWrite > 0) {
            return true;
        }
        return false;
    }
    private byte[] curReceiveData = new byte[0];
    private Thread threadReadData = new Thread(new Runnable() {
        byte[] messageByte = new byte[0];
        @Override
        public void run() {
            while (true) {
                if (didReceiveData()) {
                    LOG(TAG, "curReceiveData: -------" + curReceiveData.length);
                    LOG(TAG, "curReceiveData: -------" + ArrayUtil.toHex(curReceiveData));
                    messageByte = ArrayUtil.byteMerger(messageByte, curReceiveData);
                    Log.i(TAG, "reveiveData: -------" + messageByte.length);
                    Log.i(TAG, "reveiveData: -------" + ArrayUtil.toHex(messageByte));
//                    continue;
//                } else {
//                    if (messageByte.length == 0) {
//                        continue;
//                    }
                    Handler handler = new Handler(Looper.getMainLooper());
                    byte[] finalMessageByte = messageByte;
                    messageByte = new byte[0];
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            notifyReceive(finalMessageByte);
                        }
                    });
                }
            }
        }
    });

    private boolean didReceiveData() {
        if (mDeviceConnection == null) {
            return false;
        }
        byte[] tempByte = new byte[4096];
        int i = mDeviceConnection.bulkTransfer(inEndpoint, tempByte, tempByte.length, 1000);
        if (i > 0) {
            curReceiveData = new byte[i];
            System.arraycopy(tempByte,0, curReceiveData,0, i);
            Log.i(TAG, "didReceiveData00: -------" + ArrayUtil.toHex(curReceiveData));
            return true;
        }
        return false;
    }

    public boolean writeWithoutResponse(byte[] data) {
        LOGAAA("USB writeWithoutResponse", "----------before---dataLen" + data.length);
        int ncountWrite = mDeviceConnection.bulkTransfer(outEndpoint, data, data.length,1000);
        LOGAAA("USB writeWithoutResponse", "----------after");

        LOG(TAG, "USB writeWithoutResponse ncountWrite: --------" + ncountWrite);
        LOG(TAG, "USB writeWithoutResponse length: --------" + data.length);
        if (ncountWrite > 0) {
            return true;
        }
        return false;
    }

    private void notifyReceive(byte[] data) {
        BaseMessage baseMessage = new BaseMessage();
        baseMessage.setPush(true);
        baseMessage.setMsgContent(data);
        if (mConnectionListener != null) {
            mConnectionListener.onDataReceived(baseMessage);
        }
    }

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        @SuppressLint("NewApi")
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    mContext.unregisterReceiver(mUsbReceiver);
                    UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        LOG(TAG, "BroadcastReceiver: -----------同意权限");
                        mUsbDevice = device;
                        boolean getInterface = getInterface();
                        boolean getPoint = getPoint();
                        boolean openDevice = openDevice();
                        LOG(TAG, "getInterface: -----------" + getInterface);
                        LOG(TAG, "getPoint: -----------" + getPoint);
                        LOG(TAG, "openDevice: -----------" + openDevice);
                        if (getInterface && getPoint && openDevice) {
                            mConnectionListener.onStatusChanged(mHmDevice, BES_CONNECT_SUCCESS, DeviceProtocol.PROTOCOL_USB);
                        } else {
                            mConnectionListener.onStatusChanged(mHmDevice, BES_CONNECT_ERROR, DeviceProtocol.PROTOCOL_USB);
                        }
                    } else {
                        //用户点击拒绝
                        mConnectionListener.onStatusChanged(mHmDevice, BES_CONNECT_ERROR, DeviceProtocol.PROTOCOL_USB);
                    }
                }
            }
        }
    };

    private static final String ACTION_USB_PERMISSION = "com.android.usb.USB_PERMISSION";

    private void getUsbPermission(UsbDevice mUSBDevice) {
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        mContext.registerReceiver(mUsbReceiver, filter);
        mUsbManager.requestPermission(mUSBDevice, pendingIntent);
    }

    private static boolean getInterface() {
        LOGAAA("getInterface", "----------");
        if (mUsbDevice != null) {
            for (int i = 0; i < mUsbDevice.getInterfaceCount(); i ++) {
                LOGAAA("getInterface", "i----------" + i);
                UsbInterface intf = mUsbDevice.getInterface(i);
                for (int j = 0; j < intf.getEndpointCount(); j ++) {
                    LOGAAA("getInterface", "j----------" + j);
                    UsbEndpoint tempEndpoint = intf.getEndpoint(j);
                    LOGAAA("getInterface", "tempEndpoint---------" + tempEndpoint);
                    if (tempEndpoint != null) {
                        LOGAAA("getInterface", "getEndpointNumber---------" + tempEndpoint.getEndpointNumber());
                        if (tempEndpoint.getEndpointNumber() == 4) {
                            mInterface = intf;
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private static boolean getPoint() {
        for (int i = 0; i < mInterface.getEndpointCount(); i ++) {
            UsbEndpoint tempEndpoint = mInterface.getEndpoint(i);
            if (tempEndpoint != null) {
                if (tempEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
                    inEndpoint = tempEndpoint;
                } else if (tempEndpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
                    outEndpoint = tempEndpoint;
                }
            }
        }
        if (inEndpoint != null && outEndpoint != null) {
            return true;
        }
        return false;
    }

    private static boolean openDevice() {
        if (mUsbDevice == null) return false;
        UsbDeviceConnection conn = mUsbManager.openDevice(mUsbDevice);
        if (conn == null) {
            return false;
        }
        if (conn.claimInterface(mInterface, true)) {
            mDeviceConnection = conn;
            return true;
        } else {
            conn.close();
        }
        return false;
    }

    public static void scanDevice(Context context, ScanManager.ScanListener scanListener) {
        if (mUsbManager == null) {
            mUsbManager = (UsbManager) context.getSystemService(USB_SERVICE);
        }
        deviceList = mUsbManager.getDeviceList();
        if (!deviceList.isEmpty()) {
            for (UsbDevice device : deviceList.values()) {
                HmDevice hmDevice = new HmDevice();
                hmDevice.setDeviceName(device.getProductName());
                hmDevice.setDevicePid(device.getProductId() + "");
                hmDevice.setDeviceVid(device.getVendorId() + "");
                scanListener.onScanResult(hmDevice);
            }
        }
    }
    public void LOG(String TAG, String msg) {
        if (mContext == null) {
            return;
        }
        Log.i(TAG, "LOG:" + msg);
        String saveLogName = (String) SPHelper.getPreference(mContext, BesSdkConstants.BES_SAVE_LOG_NAME, "");
        boolean saveLog = (boolean) SPHelper.getPreference(mContext, BesSdkConstants.BES_SAVE_LOG_KEY, BesSdkConstants.BES_SAVE_LOG_VALUE);
        if (saveLog) {
            if (saveLogName.equals(BES_SAVE_LOG_OTA)) {
                LogUtils.writeForOTA(TAG, msg, saveLogName);
            } else if (saveLogName.length() > 0){
                LogUtils.writeForLOG(TAG, msg, saveLogName);
            }
        }
    }

    public static void LOGAAA(String TAG, String msg) {
        if (mContext == null) {
            return;
        }
        Log.i(TAG, "LOG:" + msg);
        String saveLogName = (String) SPHelper.getPreference(mContext, BesSdkConstants.BES_SAVE_LOG_NAME, "");

            if (saveLogName.equals(BES_SAVE_LOG_OTA)) {
                LogUtils.writeForOTA(TAG, msg, saveLogName);
            } else if (saveLogName.length() > 0){
                LogUtils.writeForLOG(TAG, msg, saveLogName);
            }

    }
}
